package com.dries.chat.netty;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.dries.chat.enums.MsgActionEnum;
import com.dries.chat.service.IChatMsgService;
import com.dries.chat.vo.ChatMsgVo;
import com.dries.chat.vo.DataContent;
import com.dries.common.utils.SpringUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Description: TextWebSocketFrame类型，表示一个文本帧
 * @Author ZSY
 * @createTime 2020/7/8 10:22
 */
@Slf4j
@Component
public class WSServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private final IChatMsgService chatMsgService = (IChatMsgService) SpringUtil.getBean("chatMsgService");

    // 定义一个channel组，管理所有的channel
    // GlobalEventExecutor 是全局的事件执行器，是一个单例
    public static final ChannelGroup channelGroup =
            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    // handlerAdded 表示连接建立，第一个被执行
    // 将当前channel 加入到 channelGroup
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

        chatMsgService.sendAddNotify(channelGroup, ctx.channel());
        // id表示唯一的值，LongText唯一   ShortText不唯一
        // log.info("【handlerAdded 被调用】" + ctx.channel().id().asLongText());
        // log.info("【handlerAdded 被调用】" + ctx.channel().id().asShortText());
    }

    // 表示channel 处于活动状态，提示 上线
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().remoteAddress() + formatter.format(LocalDateTime.now()) + "上线了~");
    }

    // 表示channel 处于不活动状态，提示 下线
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().remoteAddress() + formatter.format(LocalDateTime.now()) + "下线了~");
    }

    // 断开连接，将用户离开信息推送给当前在线的用户
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

        chatMsgService.sendRemoveNotify(channelGroup,ctx.channel());

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        // 关闭通道
        // ctx.channel().close();
        AttributeKey<String> key = AttributeKey.valueOf("user");
        String userId = ctx.channel().attr(key).get();
        UserChannelRel.remove(userId);
        ctx.channel().close();
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame)
            throws Exception {
        // 获取到当前channel
        Channel channel = ctx.channel();

        // 1.获取客户端发送的消息
        String msg = frame.text();
        DataContent dataContent = JSONUtil.toBean(msg, DataContent.class);

        // 2.判断消息类型，根据不同的类型处理不同的业务
        Integer action = dataContent.getAction();
        if (Objects.equals(action, MsgActionEnum.CONNECT.type)) {
            // 2.1 第一次连接
            chatMsgService.sendNotify(null, dataContent, channel);
        } else if (Objects.equals(action, MsgActionEnum.CHAT.type)) {
            // 2.2 点对点聊天类型的消息，把聊天记录保存到数据库，同时标记消息的签收状态
            chatMsgService.sendMessage(channelGroup, channel, dataContent);
        } else if (Objects.equals(action, MsgActionEnum.GROUP_CHAT.type)){
            // 2.3 群组聊天类型的消息，把聊天记录保存到数据库，同时标记消息的签收状态
            chatMsgService.sendGroupMessage(channelGroup, channel, dataContent);
        } else if (Objects.equals(action, MsgActionEnum.SIGNED.type)) {
            ChatMsgVo chat = dataContent.getChatMsg();
            String senderId = chat.getSenderId();
            if (UserChannelRel.get(senderId) != channel) {
                // 2.3 签收消息类型，针对具体的消息进行签收，修改数据库对应消息的签收状态
                // 保存消息到数据库，并且标记为签收
                // 扩展字段在signed类型的消息中，代表需要去签收的消息ID，都好间隔
                String msgIdsStr = dataContent.getExtand();
                log.info("========================>" + msgIdsStr);
                String[] msgIds = msgIdsStr.split(",");
                List<String> msgIdsList = new ArrayList<>();
                for (String mid : msgIds) {
                    if (StrUtil.isNotBlank(mid)) {
                        msgIdsList.add(mid);
                    }
                }
                log.info("【消息集合】========》{}", msgIdsList.toString());
                if (!msgIdsList.isEmpty()) {
                    // 批量签收
                    chatMsgService.updateMsgSigned(msgIdsList);
                }
            }
        } else if (Objects.equals(action, MsgActionEnum.KEEPALIVE.type)) {
            // 2.4 心跳类型的消息
            log.info("收到来自channel为【" + channel + "】的心跳包");
        }
    }
}

// 遍历channelGroup,根据不同的情况，回送不同的信息
//                channelGroup.forEach(e -> {
//                        if (channel != e) {
//                        // 不是自己的channel，转发消息
//                        e.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(chat)));
//                        } else {
//                        // 回显自己发送的消息
//                        e.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(chat)));
//                        }
//                        });